package com.hm.health.records.ecgpatch;

import android.util.Log;

import com.goodix.utils.EncryptUtil;
import com.goodix.utils.TimeUtil;
import com.hm.health.entity.EcgFlashRecordBean;
import com.hm.health.entity.dao.EcgPatchCsvInfo;

import org.litepal.crud.DataSupport;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class EcgPatchRecordReader {
    private static final String TAG = EcgPatchRecordReader.class.getSimpleName();
    private List<EcgFlashRecordBean> mRecordBeanList;
    private boolean isRecordLoad = false;
    protected static EcgPatchRecordReader INSTANCE = null;

    public static EcgPatchRecordReader getInstance() {
        if (INSTANCE == null) {
            synchronized (EcgPatchRecordReader.class) {
                if (INSTANCE == null) {
                    INSTANCE = new EcgPatchRecordReader();
                }
            }
        }
        return INSTANCE;
    }

    protected EcgPatchRecordReader() {
        mRecordBeanList = new ArrayList<>();
    }

    public void loadRecordBeanFromDB(String date) {
        // load from db
        long dateStart = TimeUtil.getEcgDate(date);
        long dateEnd = dateStart + 24 * 60 * 60 * 1000;

        List<EcgPatchCsvInfo> dataList = DataSupport
                .where("beginTimeStamp >= ? and endTimeStamp < ?", dateStart + "", dateEnd + "")
                .order("beginTimeStamp asc")
                .find(EcgPatchCsvInfo.class);

        Log.d(TAG, "Query ecg record from db in [" + dateStart + ", " + dateEnd + ") result " + dataList.size());

        Map<String, EcgFlashRecordBean> allTestRecord = new HashMap<>();
        List<String> allTestKey = new ArrayList<>();

        for (EcgPatchCsvInfo info : dataList) {
            EcgFlashRecordBean recordBean = null;
            if (allTestRecord.containsKey(info.getTestId())) {
                recordBean = allTestRecord.get(info.getTestId());
            }

            if (recordBean == null) {
                recordBean = new EcgFlashRecordBean();
                recordBean.setTestRecordId(info.getTestId());
                allTestKey.add(info.getTestId());
            }

            EcgFlashRecordBean.CsvFile oneCsv = new EcgFlashRecordBean.CsvFile(info.getBeginTimeStamp(), info.getEndTimeStamp(), info.getResultFilePath(), info.getRawdataFilePath(), info.getCsvId(), info.getHasAlgoResult());
            recordBean.appendRecord(oneCsv);

            allTestRecord.put(info.getTestId(), recordBean);
        }

        for (String key : allTestKey) {
            mRecordBeanList.add(allTestRecord.get(key));
        }

        isRecordLoad = true;
    }

    public void unloadRecordBeanList() {
        mRecordBeanList.clear();
        isRecordLoad = false;
    }

    public boolean isRecordLoaded() {
        return isRecordLoad;
    }

    public List<EcgFlashRecordBean> getRecordBeanList() {
        return mRecordBeanList;
    }

    public CurrentHourData loadDataList(String testRecordId, int testCsvId, long time) {
        CurrentHourData data = new CurrentHourData();

        if (!mRecordBeanList.isEmpty()) {
            for (EcgFlashRecordBean bean : mRecordBeanList) {
                if (bean.getTestRecordId().equals(testRecordId)) {

                    // load record file
                    List<Integer> hrvList = new ArrayList<>();

                    for (int i=0; i<bean.getRecordNum(); i++) {
                        EcgFlashRecordBean.CsvFile csvFile = bean.getRecord(i);
                        if (testCsvId == csvFile.csvId && time == csvFile.startTimeStamp) {
                            processOneFrameDataFile(data, csvFile.recordPath, hrvList);
                            data.mStartTimestamp = csvFile.startTimeStamp;
                            data.mEndTimestamp = csvFile.endTimeStamp;
                            break;
                        }
                    }

                    data.mName = bean.getTesterName();
                    data.mScene = bean.getmTestScene();
                    data.mHrv = calHrAvg(hrvList);
                    break;
                }
            }
        }

        return data;
    }

    private void processOneFrameDataFile(CurrentHourData curData, String fileName, List<Integer> hrvList) {
        String filePath = fileName;
        File file = new File(filePath);
        Log.i(TAG, "fileName=" + fileName);

        if (file.isFile() && file.exists()) {
            try {
                InputStreamReader isr = new InputStreamReader(new FileInputStream(file));
                BufferedReader br = new BufferedReader(isr);
                String line;
                int ignoreHeader = 0;
                while ((line = br.readLine()) != null) {
                    if (ignoreHeader < 2) {
                        // 跳过文件头,2行
                        ignoreHeader++;
                        continue;
                    }
                    String frame = line;
                    if(line.endsWith("\n")) {
                        frame = line.substring(0, line.length() - 1);
                    }
                    if (!frame.isEmpty() && !frame.contains(",")) {
                        //加密数据先解密
                        frame = EncryptUtil.decrypt(frame);
                    }
                    if (!frame.isEmpty() && frame.contains(",")) {
                        String[] valueStrs = frame.split(",");
                        curData.mTimestamps.add(Long.parseLong(valueStrs[0]));
                        //String[] dataStrs = Arrays.copyOfRange(valueStrs, 3, valueStrs.length);
                        curData.mDatas.add(Integer.parseInt(valueStrs[3]));
                        hrvList.add(Integer.parseInt(valueStrs[4]));
                        curData.mLeadState.add(Integer.parseInt(valueStrs[5]));
                    }
                }
                isr.close();
                br.close();
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        } else {
            Log.e(TAG, "no fileName=" + fileName);
        }
    }

    private int calHrAvg(List<Integer> hrvList) {
        if (hrvList == null || hrvList.isEmpty()) {
            return 0;
        }

        long sum = 0;
        int count = 0;
        for (Integer value : hrvList) {
            if (value != 0) {
                count++;
                sum += value;
            }
        }

        if (count == 0) {
            return 0;
        }

        return (int)(sum/count);
    }

    public class CurrentHourData {
        public List<Integer> mDatas;
        public List<Integer> mLeadState;
        public List<Long> mTimestamps;
        public int mHrv;
        public long mStartTimestamp;
        public long mEndTimestamp;
        public int mType;
        public String mName;
        public String mScene;
        public int mFrequency = 500;

        public CurrentHourData() {
            mDatas = new ArrayList<>();
            mLeadState = new ArrayList<>();
            mTimestamps = new ArrayList<>();
        }

        // 根据lead变化和时间戳插入空白数据
        public void insertBlankByLead() {
            if (mDatas.size() != mLeadState.size() || mDatas.size() != mTimestamps.size()) {
                // invalid data
                return;
            }

            if (mDatas.size() <= 0) {
                return;
            }

            for (int j=mLeadState.size()-1; j>=1; j--) {
                int lead = mLeadState.get(j);
                if (lead == 1) {
                    // 1: lead on; 2: lead off

                    // 2ms per frame
                    int blankSize = (int)((mTimestamps.get(j) - mTimestamps.get(j - 1)) / 2);
                    if (blankSize > 0) {
                        Log.d(TAG, "Insert blank at " + j + " count " + blankSize);
                        for (int i=0; i<blankSize; i++) {
                            mDatas.add(j, 0);
                            mLeadState.add(j, 0);
                            mTimestamps.add(j, (long)0);
                        }
                    }
                }
            }
        }
    }
}
